---------------------------------------------------------------------------------------
--			CREDITS:
--			Thanks to TotemTracker for all the Basecode
--			And Just the wonderful Idea with Bufftime
---------------------------------------------------------------------------------------
-- The "ttr" table acts as a "namespace" of sorts to help prevent conflicts with other addons
---------------------------------------------------------------------------------------
-- Local function references (for efficiency)
-- ttr_UpdateLocalFuncRefs() can be called by other addons to force TotemTracker
-- to update its local copies (in case those functions were hooked).

local GetSpellName;
local BuffFrame_UpdateDuration;
local GetSpellIDByName;
local GetSpellTexture;

function ttr_UpdateLocalFuncRefs()
	GetSpellName = getfenv(0).GetSpellName;
	BuffFrame_UpdateDuration = getfenv(0).BuffFrame_UpdateDuration;
	GetSpellIDByName = getfenv(0).GetSpellIDByName;
	GetSpellTexture = getfenv(0).GetSpellTexture;
end

-- Localize the update function and call it to initialize local references
local ttr_UpdateLocalFuncRefs = ttr_UpdateLocalFuncRefs;
ttr_UpdateLocalFuncRefs();
---------------------------------------------------------------------------------------
-- Header
_ttr = {
	localUpdateHooks = { };
};
local ttr = _ttr;

ttr.UpdateLocalReferences = function()
	local i,v;
	for i,v in ipairs(ttr.localUpdateHooks) do
		if type(v)=="function" then
			v();
		end
	end
end

--Data
-------------------------------------------------------------------------------
-- GLOBAL DECLARATIONS
-- Duration of the totem, in seconds
ttr.totemTotalTime = {
	["Disease Cleansing Totem"]			= 120;
	["Earthbind Totem"]					= 45;
	["Earth Elemental Totem(Rank 1)"]	= 120;
	["Fire Elemental Totem(Rank 1)"]	= 120;
	["Fire Nova Totem(Rank 1)"]			= 4;
	["Fire Nova Totem(Rank 2)"]			= 4;
	["Fire Nova Totem(Rank 3)"]			= 4;
	["Fire Nova Totem(Rank 4)"]			= 4;
	["Fire Nova Totem(Rank 5)"]			= 4;
	["Fire Nova Totem(Rank 6)"]			= 4;
	["Fire Nova Totem(Rank 7)"]			= 4;
	["Fire Resistance Totem(Rank 1)"]	= 120;
	["Fire Resistance Totem(Rank 2)"]	= 120;
	["Fire Resistance Totem(Rank 3)"]	= 120;
	["Fire Resistance Totem(Rank 4)"]	= 120;
	["Flametongue Totem(Rank 1)"]		= 120;
	["Flametongue Totem(Rank 2)"]		= 120;
	["Flametongue Totem(Rank 3)"]		= 120;
	["Flametongue Totem(Rank 4)"]		= 120;
	["Flametongue Totem(Rank 5)"]		= 120;
	["Frost Resistance Totem(Rank 1)"]	= 120;
	["Frost Resistance Totem(Rank 2)"]	= 120;
	["Frost Resistance Totem(Rank 3)"]	= 120;
	["Frost Resistance Totem(Rank 4)"]	= 120;
	["Grace of Air Totem(Rank 1)"]		= 120;
	["Grace of Air Totem(Rank 2)"]		= 120;
	["Grace of Air Totem(Rank 3)"]		= 120;
	["Grounding Totem"]					= 45;
	["Healing Stream Totem(Rank 1)"]	= 120;
	["Healing Stream Totem(Rank 2)"]	= 120;
	["Healing Stream Totem(Rank 3)"]	= 120;
	["Healing Stream Totem(Rank 4)"]	= 120;
	["Healing Stream Totem(Rank 5)"]	= 120;
	["Healing Stream Totem(Rank 6)"]	= 120;
	["Magma Totem(Rank 1)"]				= 20;
	["Magma Totem(Rank 2)"]				= 20;
	["Magma Totem(Rank 3)"]				= 20;
	["Magma Totem(Rank 4)"]				= 20;
	["Magma Totem(Rank 5)"]				= 20;
	["Mana Spring Totem"]				= 24;	-- Enamored Water Spirit trinket
	["Mana Spring Totem(Rank 1)"]		= 120;
	["Mana Spring Totem(Rank 2)"]		= 120;
	["Mana Spring Totem(Rank 3)"]		= 120;
	["Mana Spring Totem(Rank 4)"]		= 120;
	["Mana Spring Totem(Rank 5)"]		= 120;
	["Mana Tide Totem"]					= 12;
	["Nature Resistance Totem(Rank 1)"]	= 120;
	["Nature Resistance Totem(Rank 2)"]	= 120;
	["Nature Resistance Totem(Rank 3)"]	= 120;
	["Nature Resistance Totem(Rank 4)"]	= 120;
	["Poison Cleansing Totem"]			= 120;
	["Searing Totem(Rank 1)"]			= 30;
	["Searing Totem(Rank 2)"]			= 35;
	["Searing Totem(Rank 3)"]			= 40;
	["Searing Totem(Rank 4)"]			= 45;
	["Searing Totem(Rank 5)"]			= 50;
	["Searing Totem(Rank 6)"]			= 55;
	["Searing Totem(Rank 7)"]			= 60;
	["Sentry Totem"]					= 300;
	["Stoneclaw Totem(Rank 1)"]			= 15;
	["Stoneclaw Totem(Rank 2)"]			= 15;
	["Stoneclaw Totem(Rank 3)"]			= 15;
	["Stoneclaw Totem(Rank 4)"]			= 15;
	["Stoneclaw Totem(Rank 5)"]			= 15;
	["Stoneclaw Totem(Rank 6)"]			= 15;
	["Stoneclaw Totem(Rank 7)"]			= 15;
	["Stoneskin Totem(Rank 1)"]			= 120;
	["Stoneskin Totem(Rank 2)"]			= 120;
	["Stoneskin Totem(Rank 3)"]			= 120;
	["Stoneskin Totem(Rank 4)"]			= 120;
	["Stoneskin Totem(Rank 5)"]			= 120;
	["Stoneskin Totem(Rank 6)"]			= 120;
	["Stoneskin Totem(Rank 7)"]			= 120;
	["Stoneskin Totem(Rank 8)"]			= 120;
	["Strength of Earth Totem(Rank 1)"]	= 120;
	["Strength of Earth Totem(Rank 2)"]	= 120;
	["Strength of Earth Totem(Rank 3)"]	= 120;
	["Strength of Earth Totem(Rank 4)"]	= 120;
	["Strength of Earth Totem(Rank 5)"]	= 120;
	["Strength of Earth Totem(Rank 6)"]	= 120;
	["Totem of Wrath(Rank 1)"]			= 120;
	["Tranquil Air Totem"]				= 120;
	["Tremor Totem"]					= 120;
	["Windfury Totem(Rank 1)"]			= 120;
	["Windfury Totem(Rank 2)"]			= 120;
	["Windfury Totem(Rank 3)"]			= 120;
	["Windfury Totem(Rank 4)"]			= 120;
	["Windfury Totem(Rank 5)"]			= 120;
	["Windwall Totem(Rank 1)"]			= 120;
	["Windwall Totem(Rank 2)"]			= 120;
	["Windwall Totem(Rank 3)"]			= 120;
	["Windwall Totem(Rank 4)"]			= 120;
	["Wrath of Air Totem(Rank 1)"]		= 120;
};

-- Totem type
ttr.totemType = {
	["Disease Cleansing Totem"]	= "Water";
	["Earthbind Totem"]			= "Earth";
	["Earth Elemental Totem"]	= "Earth";
	["Fire Elemental Totem"]	= "Fire";
	["Fire Nova Totem"]			= "Fire";
	["Fire Resistance Totem"]	= "Water";
	["Flametongue Totem"]		= "Fire";
	["Frost Resistance Totem"]	= "Fire";
	["Grace of Air Totem"]		= "Air";
	["Grounding Totem"]			= "Air";
	["Healing Stream Totem"]	= "Water";
	["Magma Totem"]				= "Fire";
	["Mana Spring Totem"]		= "Water";
	["Mana Tide Totem"]			= "Water";
	["Nature Resistance Totem"]	= "Air";
	["Poison Cleansing Totem"]	= "Water";
	["Searing Totem"]			= "Fire";
	["Sentry Totem"]			= "Air";
	["Stoneclaw Totem"]			= "Earth";
	["Stoneskin Totem"]			= "Earth";
	["Strength of Earth Totem"]	= "Earth";
	["Totem of Wrath"]			= "Fire";
	["Tranquil Air Totem"]		= "Air";
	["Tremor Totem"]			= "Earth";
	["Windfury Totem"]			= "Air";
	["Windwall Totem"]			= "Air";
	["Wrath of Air Totem"]		= "Air";
};

-- Starting health of the totem
ttr.totemHealth = {
	["default"]					= 5;
	["Stoneclaw Totem(Rank 1)"]	= 50;
	["Stoneclaw Totem(Rank 2)"]	= 150;
	["Stoneclaw Totem(Rank 3)"]	= 220;
	["Stoneclaw Totem(Rank 4)"]	= 280;
	["Stoneclaw Totem(Rank 5)"]	= 390;
	["Stoneclaw Totem(Rank 6)"]	= 480;
	["Stoneclaw Totem(Rank 7)"]	= 1315;
};

-- Maps buff names to the totem that grants the buff
ttr.buff2Totem = {
	["Grounding Totem Effect"]		= "Grounding Totem";
	["Strength of Earth"]			= "Strength of Earth Totem";
	["Grace of Air"]				= "Grace of Air Totem";
	["Stoneskin"]					= "Stoneskin Totem";
	["Windwall"]					= "Windwall Totem";
	["Healing Stream"]				= "Healing Stream Totem";
	["Mana Spring"]					= "Mana Spring Totem";
	["Frost Resistance"]			= "Frost Resistance Totem";
	["Fire Resistance"]				= "Fire Resistance Totem";
	["Nature Resistance"]			= "Nature Resistance Totem";
	["Mana Tide"]					= "Mana Tide Totem";
	["Tranquil Air"]				= "Tranquil Air Totem";
	["Wrath of Air Totem"]			= "Wrath of Air Totem";
	["Totem of Wrath"]				= "Totem of Wrath";
};

--Table provided to prevent indexing errors before values are initialized
ttr.totemInfo = {
	player = {
		Earth = { };
		Fire = { };
		Water = { };
		Air = {	};
	};
	party1 = {
		Earth = { };
		Fire = { };
		Water = { };
		Air = {	};
	};
	party2 = {
		Earth = { };
		Fire = { };
		Water = { };
		Air = {	};
	};
	party3 = {
		Earth = { };
		Fire = { };
		Water = { };
		Air = {	};
	};
	party4 = {
		Earth = { };
		Fire = { };
		Water = { };
		Air = {	};
	};
};
-- Config

---------------------------------------------------------------------------------------
-- SEMI-GLOBAL FUNCTION DEFINITIONS

ttr.initConfig = function()
	if type(ttr_config)=="nil" then ttr_config = { }; end
	if type(ttr_config.buffMode) == "nil" then ttr_config.buffMode = false; end
end

-- Util
-------------------------------------------------------------------------------
-- LOCAL REFERENCE DECLARATIONS

local localUpdateHook = function()

end
ttr.localUpdateHooks[(#(ttr.localUpdateHooks))+1] = localUpdateHook;

---------------------------------------------------------------------------------------
-- LOCAL FUNCTION DECLARATIONS

local extractNum;
local GetSpellIDByName;
local getTotemTimeRemainingByType;
local getTotemTimeRemainingByName;
local clearTotemType;
local clearAllTotemTypes;
local enumTotemDuration;

---------------------------------------------------------------------------------------
-- SEMI-GLOBAL FUNCTION DEFINITIONS


ttr.extractNum = function(str,si)
	local index=si;
	local sindex,num,goodnum,found;
	if type(str)=="string" then
		if type(index) ~= "number" then
			index = 1;
		end
		sindex = index;
		while index <= strlen(str) do
			num = tonumber(strsub(str,sindex,index));
			if type(num) == "nil" then
				if found then
					return goodnum,index;
				end
				sindex = index + 1;
			else
				goodnum = num;
				found = true;
			end
			index = index + 1;
		end
	end
	return goodnum,nil;
end
extractNum = ttr.extractNum;

ttr.GetSpellIDByName = function(name,booktype)
	local i,curname,currank;

	for i=1,1000 do		-- Don't want it to loop endlessly by accident...
		curname, currank = GetSpellName(i,booktype);
		if curname then
			if currank and currank~="" then curname = curname.."("..currank..")"; end
			if name == curname then
				return i;
			end
		end
	end
	return nil;
end
GetSpellIDByName = ttr.GetSpellIDByName;

ttr.getTotemTimeRemainingByType = function(typeref)
	local t;

	if type(typeref)=="table" and type(typeref.timeStart)=="number" and type(typeref.timeTotal)=="number" then
		local tcur = GetTime();

		t = typeref.timeStart + typeref.timeTotal - tcur;
		if (t < 0) then t=nil; end
	end
	return t;
end
getTotemTimeRemainingByType = ttr.getTotemTimeRemainingByType;

ttr.getTotemTimeRemainingByName = function(playerref,tname)
	local t;

	if type(playerref)=="table" then
		local ttype = totemType[tname];
		if ttype and type(playerref[ttype])=="table" then
			if (playerref[ttype].totemName == tname) then
				t = getTotemTimeRemainingByType(playerref[ttype]);
			end
		end
	end
	return t;
end
getTotemTimeRemainingByName = ttr.getTotemTimeRemainingByName;

ttr.clearTotemType = function(typeref)
	local k,v;
	for k,v in pairs(typeref) do
		typeref[k] = nil;
	end
end
clearTotemType = ttr.clearTotemType;

ttr.clearAllTotemTypes = function(playerref)
	if type(playerref)=="table" then
		local k,v;
		for k,v in pairs(playerref) do
			clearTotemType(v);
		end
	end
end
clearAllTotemTypes = ttr.clearAllTotemTypes;


ttr.enumTotemDuration = function()
	for k,v in pairs(totemDuration) do
		DEFAULT_CHAT_FRAME:AddMessage(tostring(k)..": "..tostring(v));
	end
end
enumTotemDuration = ttr.enumTotemDuration;

-- Core -------------------------------------------------------------------------------
-- LOCAL REFERENCE DECLARATIONS

local totemInfo;
local totemTotalTime;
local totemType;
local totemHealth;
local clearTotemType;
local clearAllTotemTypes;
local GetSpellIDByName;
local buff_OnUpdate;

local localUpdateHook = function()
	totemInfo = ttr.totemInfo;
	totemTotalTime = ttr.totemTotalTime;
	totemType = ttr.totemType;
	totemHealth = ttr.totemHealth;
	clearTotemType = ttr.clearTotemType;
	clearAllTotemTypes = ttr.clearAllTotemTypes;
	GetSpellIDByName = ttr.GetSpellIDByName;
	buff_OnUpdate = ttr.buff_OnUpdate;
end
ttr.localUpdateHooks[(#(ttr.localUpdateHooks))+1] = localUpdateHook;

-------------------------------------------------------------------------------
-- CORE EVENT GLOBAL FUNCTIONS

-- Q) Will this work as local function?  Might need this global to work with XML...
-- A) Needs to be global, but can declare it as local and link the environment
--    reference to global afterwards...
core_OnLoad = function()

	-- IMPORTANT!
	ttr.UpdateLocalReferences();
		
	this:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED");
	this:RegisterEvent("PLAYER_DEAD");
	this:RegisterEvent("PLAYER_ENTERING_WORLD");
	this:RegisterEvent("ADDON_LOADED");
	this:RegisterEvent("CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS");
	this:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE");

end

core_OnEvent = function(frame,event,arg1,arg2,arg3,arg4)

	if event == "ADDON_LOADED" then
		if arg1 == "TotemTracker" then
			ttr.setBuffMode(ttr_config.buffMode);
		end

	elseif event == "UNIT_SPELLCAST_SUCCEEDED" then

		local spell,rank = arg2,arg3;

		if type(spell)=="string" and type(rank)=="string" then
			local name=spell;

			-- Spell cast...
			if rank ~= "" then name = name.."("..rank..")"; end

			local ttt = totemTotalTime[name];
			local ttype = totemType[spell];
			if type(ttt)=="number" and type(ttype)=="string" then
				-- TOTEM CAST!!!
				totemInfo.player[ttype].timeStart = GetTime();
				totemInfo.player[ttype].timeTotal = ttt;
				totemInfo.player[ttype].totemName = spell;
				totemInfo.player[ttype].totemHealth = totemHealth[name];
				if totemHealth[name]==nil then
					totemInfo.player[ttype].totemHealth = totemHealth["default"];
				end

				local spellID = GetSpellIDByName(name,BOOKTYPE_SPELL);
				if type(spellID)=="number" then
					local texture = GetSpellTexture(spellID, BOOKTYPE_SPELL);
				end
			end

				-- Clear totems on Totemic Call
--			if spell=="Totemic Call" then
--				clearAllTotemTypes(totemInfo.player);
--			end
		end

	elseif event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS" or
			event == "CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE" then
		local k,v;
		local msg = arg1;

		for k,v in pairs(totemInfo.player) do
			if v.totemName and strfind(msg,v.totemName) then

				local strstart,strend = strfind(msg,"for ");
				if strend then
					local num = extractNum(msg,strend);
					if (type(num)=="number") then
						local newhealth = v.totemHealth - num;
						v.totemHealth = newhealth;
					end
				end

				--if v.totemName ~= "Stoneclaw Totem" then
				if v.totemHealth <= 0 then
					clearTotemType(v);
				end
			end
		end
	end
end

--local playerOnTaxi = false;
--local first = true;
core_OnUpdate = function()
--	if first==true then
--		first=false;
--	end

	-- Update totem buff durations
	buff_OnUpdate();
end

-- Buff
-------------------------------------------------------------------------------
-- LOCAL REFERENCE DECLARATIONS

-- Data
local totemInfo;
local buff2Totem;

-- Functions
local clearTotemType;
local getTotemTimeRemainingByType;

local localUpdateHook = function()
	totemInfo = ttr.totemInfo;
	buff2Totem = ttr.buff2Totem;
	clearTotemType = ttr.clearTotemType;
	getTotemTimeRemainingByType = ttr.getTotemTimeRemainingByType;
end
ttr.localUpdateHooks[(#(ttr.localUpdateHooks))+1] = localUpdateHook;

-------------------------------------------------------------------------------
-- LOCAL VARIABLE DECLARATIONS

local buffTimeRemaining = { };
local totemDuration = { };

local setBuffMode;

---------------------------------------------------------------------------------------
-- FUNCTION HOOKS
-- First, a local variable for the old function is created.  Then, a local function
-- hook is created.  Finally, the global function is updated using the global environment
-- to point to the local function.  Addons that create local references to the hooked
-- function must create the local reference AFTER this code executes.  List TotemTracker
-- under OptionalDeps in your .toc file to ensure this happens!

local GetPlayerBuffTimeLeft_old = GetPlayerBuffTimeLeft;
ttr.GetPlayerBuffTimeLeft_new = function(buffIndex) 
	local rval = GetPlayerBuffTimeLeft_old(buffIndex);
	if (type(rval) == "nil" or rval <= 0) then
		local buffname = GetPlayerBuffName(buffIndex);
		if buffname then
			local t = buffTimeRemaining[buffname];
			if t and t>0 then
				rval = t;
			end
		end
	end
	return rval;
end
GetPlayerBuffTimeLeft = ttr.GetPlayerBuffTimeLeft_new;

local BuffButton_OnUpdate_old = BuffButton_OnUpdate;
ttr.BuffButton_OnUpdate_new = function()
	local oldUntilCancelled = this.untilCancelled;
	local buffname = GetPlayerBuffName(this:GetID());
	if buffname and buffTimeRemaining[buffname] then
		this.untilCancelled = 0;
	end
	BuffButton_OnUpdate_old();
	this.untilCancelled = oldUntilCancelled;
end
BuffButton_OnUpdate = ttr.BuffButton_OnUpdate_new;

ttr.setBuffMode = function(mode)
	if mode==true then
		BuffButton_OnUpdate = ttr.BuffButton_OnUpdate_new;
		GetPlayerBuffTimeLeft = ttr.GetPlayerBuffTimeLeft_new;
	else
		BuffButton_OnUpdate = BuffButton_OnUpdate_old;
		GetPlayerBuffTimeLeft = GetPlayerBuffTimeLeft_old;
	end
end
setBuffMode = ttr.setBuffMode;

-------------------------------------------------------------------------------
-- BUFF EVENT GLOBAL FUNCTIONS

ttr.buff_OnUpdate = function()
	local k,v,k2,v2,tCur,tMin;

	for k,v in pairs(totemDuration) do
		totemDuration[k] = nil;
	end
	for k,v in pairs(totemInfo) do
		for k2,v2 in pairs(v) do
			tCur = getTotemTimeRemainingByType(v2);
			if tCur and tCur > 0 then
				tMin = totemDuration[v2.totemName];
				if tMin and tMin > 0 then tMin = min(tMin,tCur);
				else tMin = tCur;
				end
				totemDuration[v2.totemName] = tMin;
			else
				clearTotemType(v2);
			end
		end
	end
	for k,v in pairs(buff2Totem) do
		if totemDuration[v] then
			buffTimeRemaining[k] = totemDuration[v];
		else
			buffTimeRemaining[k] = nil;
		end
	end
end